/*
 * 人人信用管理有限公司版权所有.
 * 
 * @Description:
 * 
 * @author: liyuliang
 * 
 * @date: 20 Apr 2017 10:44:16 am
 * 
 * 注意：本内容仅限于人人信用管理有限公司内部传阅，禁止外泄以及用于其他的商业目的.
 */

package com.rrx360.common.config;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;



public class Config {

  public static final Logger logger = LogManager.getLogger(Config.class);

  public static final String MONITOR_INTERVAL_SECONDS = "_monitorinterval";

  public static final int DEFAULT_MONITOR_INTERVAL = 30000;

  private Configuration configuration;

  private List<ConfigNode> configNodeList;

  private volatile int monitorInterval = DEFAULT_MONITOR_INTERVAL;

  private Thread monitorThread;

  private Map<String, List<ConfigChangeListener>> listenerMap = new ConcurrentHashMap<>();

  public void addConfigNode(ConfigNode configNode) {
    if (configNodeList == null) {
      configNodeList = new ArrayList<>();
    }
    configNodeList.add(configNode);
  }

  public String getString(String key) {
    return configuration.getString(key);
  }

  public String getString(String key, String defaultValue) {
    return configuration.getString(key, defaultValue);
  }

  public Configuration getConfiguration() {
    return configuration;
  }

  public synchronized void load() {
    load(false);
  }

  public synchronized void load(boolean reload) {
    Map<String, ConfigNode> configNodeMap = new HashMap<>();
    List<String> seqNodeList = new ArrayList<>();
    traval(configNodeList, configNodeMap, seqNodeList);
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < seqNodeList.size(); i++) {
      String s = seqNodeList.get(seqNodeList.size() - 1 - i);
      builder.append(s + ":" + configNodeMap.get(s).getConfigInfo() + ",");
    }
    if (builder.length() > 1) {
      builder.deleteCharAt(builder.length() - 1);
    }
    if (!reload) {
      logger.info("load config from:" + builder.toString());
    } else {
      logger.debug("load config from:" + builder.toString());
    }
    CompositeConfiguration config = new CompositeConfiguration();
    for (int i = 0; i < seqNodeList.size(); i++) {
      ConfigNode configNode = configNodeMap.get(seqNodeList.get(seqNodeList.size() - 1 - i));
      Configuration configuration = configNode.getConfiguration();
      if (configuration != null) {
        config.addConfiguration(configuration);
      }
    }
    this.configuration = config;
    if (!reload) {
      startMonitor();
    }
  }

  public synchronized void startMonitor() {
    if (monitorThread == null) {
      monitorThread = new Thread(() -> {
        while (true) {
          try {
            String monitorIntervalString = this.configuration.getString("config.monitor.interval");
            if (StringUtils.isNotEmpty(monitorIntervalString)) {
              monitorInterval = Integer.parseInt(monitorIntervalString) * 1000;
              if (monitorInterval < 3000) {
                monitorInterval = DEFAULT_MONITOR_INTERVAL;
              }
            }
            Thread.sleep(monitorInterval);
            Configuration oldConfig = this.configuration;
            load(true);
            Configuration newConfig = this.configuration;
            triggerConfigChangeEvent(oldConfig, newConfig);
          } catch (Exception e) {
//            AlertUtil.alert(ExceptionType.SYS_EXCEPTION, "监控配置文件更新异常", e);
            try {
              Thread.sleep(DEFAULT_MONITOR_INTERVAL);
            } catch (Exception e1) {
              logger.error(e1.getMessage(), e1);
            }
          }
        }
      });
      monitorThread.setDaemon(true);
      monitorThread.start();
    }
  }

  public void addConfigChangeListener(String key, ConfigChangeListener listener) {
    List<ConfigChangeListener> list = listenerMap.get(key);
    if (list == null) {
      list = new ArrayList<>();
      listenerMap.put(key, list);
    }
    list.add(listener);
  }

  private synchronized void triggerConfigChangeEvent(Configuration oldConfig,
      Configuration newConfig) {
    Iterator<String> keys = oldConfig.getKeys();
    while (keys.hasNext()) {
      String key = keys.next();
      String oldValue = oldConfig.getString(key);
      String newValue = newConfig.getString(key);
      if (!StringUtils.equals(oldValue, newValue)) {
        logger.info("key:" + key + " changed from " + oldValue + " to " + newValue);
        ConfigChangeEvent event = new ConfigChangeEvent(oldValue, newValue, oldConfig, newConfig);
        List<ConfigChangeListener> list = this.listenerMap.get(key);
        if (list != null) {
          for (ConfigChangeListener listener : list) {
            try {
              listener.onConfigChange(event);
            } catch (Exception e) {
//              AlertUtil.alert(ExceptionType.SYS_EXCEPTION, "配置更新事件处理异常", e);
            }
          }
        }
      }
    }
  }

  public Properties getAllProperties() {
    Properties properties = new Properties();
    if (configuration != null) {
      configuration.getKeys().forEachRemaining((key) -> {
        properties.put(key, configuration.getString(key));
      });
    }
    return properties;
  }

  private void traval(List<ConfigNode> configNodeList, Map<String, ConfigNode> configNodeMap,
      List<String> seqNodeList) {
    if (configNodeList == null || configNodeList.size() == 0) {
      return;
    }
    for (int i = 0; i < configNodeList.size(); i++) {
      ConfigNode configNode = configNodeList.get(configNodeList.size() - 1 - i);
      if (configNodeMap.containsKey(configNode.getNodeId())) {
        continue;
      } else {
        configNodeMap.put(configNode.getNodeId(), configNode);
      }

      traval(configNode.getDependencyList(), configNodeMap, seqNodeList);
      seqNodeList.add(configNode.getNodeId());
    }
  }

  public void printAll() {
    Configuration properties = getConfiguration();
    Iterator<String> keys = properties.getKeys();
    StringBuilder builder = new StringBuilder();
    while (keys.hasNext()) {
      String key = keys.next();
      String value = properties.getString(key);
      if (key.toLowerCase(Locale.getDefault()).contains("password")
          || key.toLowerCase(Locale.getDefault()).contains("token")) {
        if (value.length() > 2) {
          value = value.charAt(0) + "*" + value.charAt(value.length() - 1);
        } else {
          value = "*";
        }
      }
      builder.append(key + " " + value + "\n");
    }
    logger.info("configuration:\n" + builder.toString());
  }

}
